//
//  NSAttributedStringEx.swift
//  Risac
//
//  Created by remy on 2017/12/13.
//  Copyright © 2017年 remy. All rights reserved.
//

import UIKit

extension String: RisacCompatible {}
extension RisacSpace where Base == String {
    
    public var attrWrap: RisacAttrWrapper {
        return base.attr.sac.attrWrap
    }
    
    public func attrWrap(_ closure: (RisacAttrWrapper) -> Void) -> RisacAttrWrapper {
        return base.attr.sac.attrWrap(closure)
    }
    
    public func line(height: CGFloat? = nil,
                     space: CGFloat? = nil,
                     alignment: NSTextAlignment = .left,
                     mode: NSLineBreakMode = .byTruncatingTail,
                     style: NSParagraphStyle? = nil,
                     range: NSRange? = nil,
                     closure: ((NSMutableParagraphStyle) -> Void)? = nil) -> RisacAttrWrapper {
        return self.attrWrap.line(height: height, space: space, alignment: alignment, mode: mode, style: style, range: range, closure: closure)
    }
    
    public func paragraph(_ style: NSParagraphStyle? = nil,
                          range: NSRange? = nil,
                          closure: ((NSMutableParagraphStyle) -> Void)? = nil) -> RisacAttrWrapper {
        return self.attrWrap.paragraph(style, range: range, closure: closure)
    }
    
    public func height(_ width: CGFloat,
                       font: UIFont,
                       lineHeight: CGFloat? = nil,
                       lineSpace: CGFloat? = nil) -> CGFloat {
        return internalSize(self.attrWrap.font(font).line(height: lineHeight, space: lineSpace, mode: .byWordWrapping).rawValue, CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)).height
    }
    
    public func width(font: UIFont) -> CGFloat {
        return internalSize(self.attrWrap.font(font).rawValue, CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)).width
    }
    
    public func size(_ size: CGSize,
                     font: UIFont,
                     lineHeight: CGFloat? = nil,
                     lineSpace: CGFloat? = nil) -> CGSize {
        return internalSize(self.attrWrap.font(font).line(height: lineHeight, space: lineSpace, mode: .byWordWrapping).rawValue, size)
    }
}

extension NSAttributedString: RisacCompatible {}
extension RisacSpace where Base: NSAttributedString {
    
    public var attrWrap: RisacAttrWrapper {
        let mutableAttr = NSMutableAttributedString(attributedString: base)
        return RisacAttrWrapper(rawValue: mutableAttr)
    }
    
    public func attrWrap(_ closure: (RisacAttrWrapper) -> Void) -> RisacAttrWrapper {
        let wrapper = self.attrWrap
        closure(wrapper)
        return wrapper
    }
    
    public func line(height: CGFloat? = nil,
                     space: CGFloat? = nil,
                     alignment: NSTextAlignment = .left,
                     mode: NSLineBreakMode = .byTruncatingTail,
                     style: NSParagraphStyle? = nil,
                     range: NSRange? = nil,
                     closure: ((NSMutableParagraphStyle) -> Void)? = nil) -> RisacAttrWrapper {
        return self.attrWrap.line(height: height, space: space, alignment: alignment, mode: mode, style: style, range: range, closure: closure)
    }
    
    public func paragraph(_ style: NSParagraphStyle? = nil,
                          range: NSRange? = nil,
                          closure: ((NSMutableParagraphStyle) -> Void)? = nil) -> RisacAttrWrapper {
        return self.attrWrap.paragraph(style, range: range, closure: closure)
    }
    
    public func height(_ width: CGFloat) -> CGFloat {
        return self.attrWrap.height(width)
    }
    
    public func width() -> CGFloat {
        return self.attrWrap.width()
    }
    
    public func size(_ size: CGSize) -> CGSize {
        return self.attrWrap.size(size)
    }
}

public struct RisacAttrWrapper: RawRepresentable {
    
    public typealias RawValue = NSMutableAttributedString
    public var rawValue: NSMutableAttributedString
    public var string: String { return rawValue.string }
    fileprivate var dict: [NSAttributedString.Key: Any] = [:]
    
    public init(rawValue: RawValue) { self.rawValue = rawValue }
    
    @discardableResult
    public func applying(_ attributes: [NSAttributedString.Key: Any], _ range: NSRange? = nil) -> RisacAttrWrapper {
        let range = range ?? NSRange(location: 0, length: rawValue.length)
        rawValue.addAttributes(attributes, range: range)
        return self
    }
    
    // 下划线的样式,颜色
    @discardableResult
    public func underline(_ style: NSUnderlineStyle = .single,
                          color: UIColor? = nil,
                          range: NSRange? = nil) -> RisacAttrWrapper {
        var dict: [NSAttributedString.Key: Any] = [.underlineStyle: style.rawValue]
        if let color = color { dict[.underlineColor] = color }
        return applying(dict, range)
    }
    
    // 删除线的样式,颜色
    @discardableResult
    public func strikethrough(_ style: NSUnderlineStyle = .single,
                              color: UIColor? = nil,
                              range: NSRange? = nil) -> RisacAttrWrapper {
        var dict: [NSAttributedString.Key: Any] = [.strikethroughStyle: style.rawValue]
        if let color = color { dict[.strikethroughColor] = color }
        return applying(dict, range)
    }
    
    // 空心字描边的宽度,颜色
    @discardableResult
    public func stroke(_ width: CGFloat,
                       color: UIColor,
                       range: NSRange? = nil) -> RisacAttrWrapper {
        return applying([.strokeWidth: width, .strokeColor: color], range)
    }
    
    // 倾斜度
    @discardableResult
    public func obliqueness(_ angle: CGFloat, range: NSRange? = nil) -> RisacAttrWrapper {
        return applying([.obliqueness: angle], range)
    }
    
    @discardableResult
    public func appendAttachment(_ attachment: NSTextAttachment) -> RisacAttrWrapper {
        let attr = NSAttributedString(attachment: attachment)
        rawValue.append(attr)
        return self
    }
    
    @discardableResult
    public func shadow(offset: CGSize,
                       radius: CGFloat,
                       color: UIColor,
                       range: NSRange? = nil) -> RisacAttrWrapper {
        let shadow = NSShadow()
        shadow.shadowOffset = offset
        shadow.shadowBlurRadius = radius
        shadow.shadowColor = color
        return applying([.shadow: shadow], range)
    }
    
    @discardableResult
    public func baseline(_ offset: CGFloat, range: NSRange? = nil) -> RisacAttrWrapper {
        return applying([.baselineOffset: offset], range)
    }
    
    @discardableResult
    public func paragraph(_ style: NSParagraphStyle? = nil,
                          range: NSRange? = nil,
                          closure: ((NSMutableParagraphStyle) -> Void)? = nil) -> RisacAttrWrapper {
        let mutableAttr = NSMutableParagraphStyle()
        if let style = style {
            mutableAttr.setParagraphStyle(style)
        }
        closure?(mutableAttr)
        return applying([.paragraphStyle: mutableAttr], range)
    }
    
    @discardableResult
    public func line(height: CGFloat? = nil,
                     space: CGFloat? = nil,
                     alignment: NSTextAlignment = .left,
                     mode: NSLineBreakMode = .byTruncatingTail,
                     style: NSParagraphStyle? = nil,
                     range: NSRange? = nil,
                     closure: ((NSMutableParagraphStyle) -> Void)? = nil) -> RisacAttrWrapper {
        return paragraph(style, range: range) {
            if let height = height {
                $0.minimumLineHeight = height
                $0.maximumLineHeight = height
            }
            if let space = space {
                $0.lineSpacing = space
            }
            $0.alignment = alignment
            $0.lineBreakMode = mode
            closure?($0)
        }
    }
    
    @discardableResult
    public func color(_ color: UIColor, range: NSRange? = nil) -> RisacAttrWrapper {
        return applying([.foregroundColor: color], range)
    }
    
    @discardableResult
    public func bgColor(_ color: UIColor, range: NSRange? = nil) -> RisacAttrWrapper {
        return applying([.backgroundColor: color], range)
    }
    
    @discardableResult
    public func font(_ fontName: String,
                     _ fontSize: CGFloat,
                     range: NSRange? = nil) -> RisacAttrWrapper {
        let font = UIFont(name: fontName, size: fontSize) ?? UIFont.systemFont(ofSize: fontSize)
        return applying([.font: font], range)
    }
    
    @discardableResult
    public func font(_ font: UIFont, range: NSRange? = nil) -> RisacAttrWrapper {
        return applying([.font: font], range)
    }
    
    @discardableResult
    public func systemFont(_ fontSize: CGFloat, range: NSRange? = nil) -> RisacAttrWrapper {
        return applying([.font: UIFont.systemFont(ofSize: fontSize)], range)
    }
    
    @discardableResult
    public func boldSystemFont(_ fontSize: CGFloat, range: NSRange? = nil) -> RisacAttrWrapper {
        return applying([.font: UIFont.boldSystemFont(ofSize: fontSize)], range)
    }
    
    @discardableResult
    public func italicSystemFont(_ fontSize: CGFloat, range: NSRange? = nil) -> RisacAttrWrapper {
        return applying([.font: UIFont.italicSystemFont(ofSize: fontSize)], range)
    }
    
    public func height(_ width: CGFloat) -> CGFloat {
        let size = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)
        return self.size(size).height
    }
    
    public func width() -> CGFloat {
        let size = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
        return self.size(size).width
    }
    
    public func size(_ size: CGSize) -> CGSize {
        let attr = NSMutableAttributedString(attributedString: rawValue)
        attr.enumerateAttribute(.paragraphStyle, in: NSRange(location: 0, length: attr.length)) { (attribute, range, stop) in
            guard let style = attribute as? NSParagraphStyle else { return }
            let mutableAttr = NSMutableParagraphStyle()
            mutableAttr.setParagraphStyle(style)
            // 首个获取的.paragraphStyle必须是.byWordWrapping,否则只计算单行高度
            mutableAttr.lineBreakMode = .byWordWrapping
            attr.addAttributes([.paragraphStyle: mutableAttr], range: range)
            stop.pointee = true
        }
        return internalSize(attr, size)
    }
}

@inline(__always)
private func internalSize(_ attr: NSAttributedString, _ size: CGSize) -> CGSize {
    let rect = attr.boundingRect(with: size, options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil)
    return rect.size
}

public func += (lhs: inout NSAttributedString, rhs: NSAttributedString) {
    let ns = NSMutableAttributedString(attributedString: lhs)
    ns.append(rhs)
    lhs = ns
}

public func += (lhs: inout NSAttributedString, rhs: String) {
    let ns = NSMutableAttributedString(attributedString: lhs)
    ns.append(NSAttributedString(string: rhs))
    lhs = ns
}

public func += (lhs: NSMutableAttributedString, rhs: NSAttributedString) {
    lhs.append(rhs)
}

public func += (lhs: NSMutableAttributedString, rhs: String) {
    lhs.append(NSAttributedString(string: rhs))
}

public func += (lhs: RisacAttrWrapper, rhs: NSAttributedString) {
    lhs.rawValue.append(rhs)
}

public func += (lhs: RisacAttrWrapper, rhs: String) {
    lhs.rawValue.append(NSAttributedString(string: rhs))
}

public func += (lhs: RisacAttrWrapper, rhs: RisacAttrWrapper) {
    lhs.rawValue.append(rhs.rawValue)
}

public func + (lhs: NSAttributedString, rhs: NSAttributedString) -> NSAttributedString {
    let ns = NSMutableAttributedString(attributedString: lhs)
    ns.append(rhs)
    return ns
}

public func + (lhs: NSMutableAttributedString, rhs: NSMutableAttributedString) -> NSMutableAttributedString {
    lhs.append(rhs)
    return lhs
}

public func + (lhs: NSAttributedString, rhs: String) -> NSAttributedString {
    return lhs + NSAttributedString(string: rhs)
}

public func + (lhs: String, rhs: NSAttributedString) -> NSAttributedString {
    return NSAttributedString(string: lhs) + rhs
}

public func + (lhs: NSMutableAttributedString, rhs: String) -> NSMutableAttributedString {
    return lhs + NSMutableAttributedString(string: rhs)
}

public func + (lhs: String, rhs: NSMutableAttributedString) -> NSMutableAttributedString {
    return NSMutableAttributedString(string: lhs) + rhs
}

public func + (lhs: RisacAttrWrapper, rhs: NSAttributedString) -> RisacAttrWrapper {
    return (lhs.rawValue + rhs).sac.attrWrap
}

public func + (lhs: NSAttributedString, rhs: RisacAttrWrapper) -> RisacAttrWrapper {
    return (lhs + rhs.rawValue).sac.attrWrap
}

public func + (lhs: RisacAttrWrapper, rhs: String) -> RisacAttrWrapper {
    return (lhs.rawValue + NSAttributedString(string: rhs)).sac.attrWrap
}

public func + (lhs: String, rhs: RisacAttrWrapper) -> RisacAttrWrapper {
    return (NSAttributedString(string: lhs) + rhs.rawValue).sac.attrWrap
}

public func + (lhs: RisacAttrWrapper, rhs: RisacAttrWrapper) -> RisacAttrWrapper {
    return (lhs.rawValue + rhs.rawValue).sac.attrWrap
}
